library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.processor_pkg.all;

entity alu is

	port(	in_op1		: 		in 	word_t;
			in_op2		:		in		word_t;
			in_opcode	:		in		mnemonic_t;
			in_flags		:		in		flags_t;
			out_res		:		out	word_t;
			out_flags	:		out	out_flags_t
	);
	
end entity alu;

architecture arch of alu is
begin
	process(in_op1, in_op2, in_opcode, in_flags) is
	
	variable temp_res : std_logic_vector(32 downto 0);
	variable temp_flags	:	out_flags_t;
	
	begin
		temp_res := (others => '0');
		temp_flags.flags	:= (others => '0');
		temp_flags.valid	:= (others => '0');
		case in_opcode is
			when AND_I => -- valja
				temp_res 						:= "0" & (in_op1 and in_op2);
			
			when SUB => -- valja
				temp_res 						:= std_logic_vector(unsigned(std_logic_vector'("0" & in_op1)) - unsigned(std_logic_vector'("0" & in_op2)));
				temp_flags.valid				:= "1111";
				
			when ADD => -- valja
				temp_res 						:= std_logic_vector(unsigned(std_logic_vector'("0" & in_op1)) + unsigned(std_logic_vector'("0" & in_op2)));
				temp_flags.valid				:= "1111";
				
			when ADC => -- valja
				temp_res 						:= std_logic_vector(unsigned(std_logic_vector'("0" & in_op1)) + unsigned(std_logic_vector'("0" & in_op2))
														+ sl2unsigned(in_flags(c_flag),33));
				temp_flags.valid				:= "1111";
				
			when SBC => -- valja
				temp_res 						:= std_logic_vector(unsigned(std_logic_vector'("0" & in_op1)) - unsigned(std_logic_vector'("0" & in_op2))
														- sl2unsigned(in_flags(c_flag),33));
				temp_flags.valid				:= "1111";
				
			when CMP => -- valja
				temp_res 						:= std_logic_vector(signed(std_logic_vector'("0" & in_op1)) - signed(std_logic_vector'("0" & in_op2)));
				temp_flags.flags(v_flag) 	:= (in_op1(31) xor in_op2(31)) and (in_op2(31) xnor temp_res(31));
				temp_flags.valid				:= "1111";
				
			when SSUB => -- valja
				temp_res 						:= std_logic_vector(signed(std_logic_vector'("0" & in_op1)) - signed(std_logic_vector'("0" & in_op2)));
				temp_flags.flags(v_flag) 	:= (in_op1(31) xor in_op2(31)) and (in_op2(31) xnor temp_res(31));
				temp_flags.valid				:= "1111";
				
			when SADD =>
				temp_res 						:= std_logic_vector(signed(std_logic_vector'("0" & in_op1)) + signed(std_logic_vector'("0" & in_op2)));
				temp_flags.flags(v_flag) 	:= (in_op1(31) xnor in_op2(31)) and (in_op2(31) xor temp_res(31));
				temp_flags.valid				:= "1111";
				
			when SADC =>
				temp_res 						:= std_logic_vector(signed(std_logic_vector'("0" & in_op1)) + signed(std_logic_vector'("0" & in_op2))
														+ signed(sl2unsigned(in_flags(c_flag),32)));
				temp_flags.flags(v_flag) 	:= (in_op1(31) xnor in_op2(31)) and (in_op2(31) xor temp_res(31));
				temp_flags.valid				:= "1111";
				
			when SSBC =>
				temp_res 						:= std_logic_vector(signed(std_logic_vector'("0" & in_op1)) - signed(std_logic_vector'("0" & in_op2))
														- signed(sl2unsigned(in_flags(c_flag),32)));
				temp_flags.flags(v_flag) 	:= (in_op1(31) xor in_op2(31)) and (in_op2(31) xnor temp_res(31));
				temp_flags.valid				:= "1111";
				
			when MOV => -- radi
				temp_res 						:= "0" & in_op2;
				
			when NOT_I =>	-- radi
				temp_res							:= "0" & std_logic_vector(not unsigned(in_op2));
				
			when SHL =>	-- radi
				temp_res							:=  "0" & std_logic_vector(unsigned(in_op2) sll to_integer(unsigned(in_op1)));
				
			when SHR =>	-- radi
				temp_res							:=  "0" & std_logic_vector(unsigned(in_op2) srl to_integer(unsigned(in_op1)));
				
			when ASR =>	-- radi
				temp_res							:=  "0" & to_stdlogicvector(to_bitvector(in_op2) sra to_integer(unsigned(in_op1)));
										
			when MOV_IMM => -- radi
				temp_res 						:= "0" & in_op2;
				
			when SMOV =>
				temp_res 						:= "0" & in_op2;
				
			when others =>
				temp_res							:= (others => '0');
		end case;
		
		temp_flags.flags(n_flag) 			:= temp_res(31);
		if (temp_res(31 downto 0) = "00000000000000000000000000000000") then
			temp_flags.flags(z_flag) 		:= '1';
		else
			temp_flags.flags(z_flag) 		:= '0';
		end if;
		temp_flags.flags(c_flag) 			:= temp_res(32);
		
		out_flags 								<= temp_flags;
		out_res 									<= temp_res(31 downto 0);
		
	end process;
end architecture arch;